var cordys = {};
cordys.Browser = {};

cordys.Browser.isIE = (function() {
    if (navigator.appVersion.indexOf("MSIE") != -1) return true;
    return false;
})();

cordys.createAttributeNS = function(xmlDocument, namespaceURI, qualifiedName) {
    if (xmlDocument.createAttributeNS) {
        return xmlDocument.createAttributeNS(namespaceURI, qualifiedName);
    } else {
        // IE
        return xmlDocument.createNode(2, qualifiedName, namespaceURI);
    }
}

cordys.setXMLAttribute = function(elementNode, attributeNamespace, attributeName, attributeValue) {
    var attributeNode = cordys.createAttributeNS(elementNode.ownerDocument, attributeNamespace, attributeName);
    attributeNode.nodeValue = attributeValue;
    if (elementNode.setAttributeNodeNS) {
        return elementNode.setAttributeNodeNS(attributeNode);
    } else {
        //IE
        return elementNode.setAttributeNode(attributeNode);
    }
}

cordys.selectXMLNodes = function(object, xpathExpression, namespaces) {
    if (!cordys.Browser.isIE) {
        try {
            var result = {
                length: 0
            };
            // = new XMLSelectionList(object,xpathExpression);
            var xmlDocument = (object.ownerDocument || object);
            if (namespaces) {
                var savedNamespaces = (xmlDocument.__namespaces || null);
                xmlDocument.__namespaces = namespaces;
            }
            var nodeList = xmlDocument.evaluate(xpathExpression, object, cordys.__createNSResolver(xmlDocument), XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
            var node, nodeIndex = 0;
            if (node = nodeList.iterateNext()) {
                do {
                    result[nodeIndex++] = node;
                }
                while (node = nodeList.iterateNext())
                result.length = nodeIndex;
            }
            if (namespaces) {
                xmlDocument.__namespaces = savedNamespaces;
            }
            return result;
        } catch (e) {
            throw "selectXMLNodes: expression '" + xpathExpression + "' cannot be evaluated. " + e.message;
        }
    } else {
        try {
            var xmlDocument = (object.ownerDocument || object);
            var isXSLPattern = (xmlDocument.getProperty("SelectionLanguage") === "XSLPattern");
            if (isXSLPattern) {
                xmlDocument.setProperty("SelectionLanguage", "XPath");
            }
            if (namespaces) {
                var savedNamespaces = xmlDocument.getProperty("SelectionNamespaces"); //NOMBV
                cordys.setXMLNamespaces(xmlDocument, namespaces);
            }
            var result = object.selectNodes(xpathExpression); //NOMBV
            if (namespaces) {
                xmlDocument.setProperty("SelectionNamespaces", savedNamespaces); //NOMBV
            }
            if (isXSLPattern) {
                xmlDocument.setProperty("SelectionLanguage", "XSLPattern");
            }
            return result;
        } catch (e) {
            throw "selectXMLNodes: expression '" + xpathExpression + "' cannot be evaluated. " + e.message;
        }
    }
}

cordys.selectXMLNode = function(object, xpathExpression, namespaces) {
    if (!cordys.Browser.isIE) {
        try {
            var xmlDocument = (object.ownerDocument || object);
            if (namespaces) {
                var savedNamespaces = (xmlDocument.__namespaces || null);
                xmlDocument.__namespaces = namespaces;
            }
            var result = xmlDocument.evaluate(xpathExpression, object, cordys.__createNSResolver(xmlDocument), XPathResult.FIRST_ORDERED_NODE_TYPE, null);
            if (namespaces) {
                xmlDocument.__namespaces = savedNamespaces;
            }
            if (result) return result.singleNodeValue;
            return null;
        } catch (e) {
            throw "selectXMLNode: expression '" + xpathExpression + "' cannot be evaluated. " + e.message;
        }
    } else {
        try {
            var xmlDocument = (object.ownerDocument || object);
            var isXSLPattern = (xmlDocument.getProperty("SelectionLanguage") === "XSLPattern");
            if (isXSLPattern) {
                xmlDocument.setProperty("SelectionLanguage", "XPath");
            }
            if (namespaces) {
                var savedNamespaces = xmlDocument.getProperty("SelectionNamespaces"); //NOMBV
                cordys.setXMLNamespaces(xmlDocument, namespaces);
            }
            var result = object.selectSingleNode(xpathExpression); //NOMBV
            if (namespaces) {
                xmlDocument.setProperty("SelectionNamespaces", savedNamespaces); //NOMBV
            }
            if (isXSLPattern) {
                xmlDocument.setProperty("SelectionLanguage", "XSLPattern");
            }
            return result;
        } catch (e) {
            throw "selectXMLNode: expression '" + xpathExpression + "' cannot be evaluated. " + e.message;
        }
    }
}

cordys.getTextContent = function(node) {
    if (node != null) {
        var text = node.textContent || node.text;
        return text ? text : "";
    } else {
        return null;
    }
}

cordys.setTextContent = function(node, value) {
    if (node != null) {
        if (cordys.Browser.isIE) // ie
        node.text = value;
        else node.textContent = value;
    }
}

cordys.setNodeText = function(node, xpath, value, namespaces) {
    if (node && (node = cordys.selectXMLNode(node, xpath, namespaces))) {
        cordys.setTextContent(node, value);
        return true;
    }
    return false;
}

cordys.getNodeText = function(node, xpath, defaultValue, namespaces) {
    if (node && (node = cordys.selectXMLNode(node, xpath, namespaces))) {
        return (cordys.getTextContent(node) || defaultValue || "");
    }
    return defaultValue;
}

cordys.parseStringToXML = function(sXML) {
    if (!cordys.Browser.isIE) {
        var parser = new DOMParser();
        try {
            return getFeedsRequest = parser.parseFromString(sXML, "application/xml");
        } catch (err) {
            cordys.notify("error", err.description, "Parsing Error");
            return false;
        }
    } else {
        try {
            var xmlDoc = new ActiveXObject("MSXML2.DOMDocument.6.0");
            xmlDoc.setProperty("SelectionLanguage", "XPath");
            xmlDoc.async = false;
            xmlDoc.loadXML(sXML);
            return xmlDoc;
        } catch (err) {
            cordys.notify("error", err.description, "Parsing Error");
            return false;
        }
    }
}

cordys.getXMLDocument = function() {
    if (cordys.Browser.isIE) return new ActiveXObject("MSXML2.DOMDocument.6.0"); //NOMBV
    else return document.implementation.createDocument("", "", null);
}

cordys.cloneXMLDocument = function(doc) {
    var clonedDoc = cordys.getXMLDocument();
    if (doc.documentElement) {
        var docFragment = clonedDoc.createDocumentFragment();
        docFragment.appendChild(clonedDoc.importNode(doc.documentElement, true));
        clonedDoc.appendChild(docFragment);
    }
    cordys.setXMLNamespaces(clonedDoc, cordys.getXMLNamespaces(doc));
    return clonedDoc;
}

cordys.setXMLNamespaces = function(object, namespaces) {
    if (!cordys.Browser.isIE) {
        var xmlDocument = (object.ownerDocument || object);
        xmlDocument.__namespaces = namespaces;
    } else {
        var xmlDocument = (object.ownerDocument || object),
            res = "";
        for (var prefix in namespaces) {
            if (prefix.indexOf("xml") == 0 || !namespaces[prefix]) continue;
            if (res.length > 0) res += " ";
            res += "xmlns:" + prefix + "='" + namespaces[prefix] + "'";
        }
        xmlDocument.setProperty("SelectionNamespaces", res); //NOMBV
    }
}

cordys.getXMLNamespaces = function(object) {
    var xmlDocument = (object.ownerDocument || object);
    return (xmlDocument.__namespaces || null);
}

cordys.getXML = function(node) {
    if (cordys.Browser.isIE) return node.xml;
    else return (new XMLSerializer()).serializeToString(node);
}

cordys.__createNSResolver = function(xmlDocument) {
    if (xmlDocument.__namespaces) {
        return function(prefix) {
            return xmlDocument.__namespaces[prefix] || null;
        }
    }
    return null;
};

cordys.loadXMLDocument = function(xmlString, xmlDocument) {
    if (!xmlDocument) xmlDocument = cordys.getXMLDocument();

    if (xmlDocument) {
        if (cordys.Browser.isIE) {
            xmlDocument.loadXML(xmlString); //NOMBV
            return xmlDocument;
        }

        setReadyState(xmlDocument, 1);
        xmlDocument.parseError = {
            "errorCode": 0,
            "reason": ""
        };
        // get a new XML document if the xml contained is a dummy node with nothing inside e.g. <xml id="xmlID"/> //NOMBV
        // the issue resolved is 'no element found' error - but this could save already existing forms with that issue
        var oDoc = (xmlString.replace(/\s/g, "") != "") ? (new DOMParser()).parseFromString(xmlString, "text/xml") : this.getXMLDocument();
        setReadyState(xmlDocument, 2);
        if (!xmlDocument || !xmlDocument.preserveWhiteSpace) cleanWhitespace(oDoc.documentElement);
        var node = xmlDocument.firstChild;
        // Cleanup "to" document
        while (node) {
            xmlDocument.removeChild(node);
            node = xmlDocument.firstChild;
        }
        // Exchange nodes..
        if (node = oDoc.firstChild) {
            var xmlDocument = (xmlDocument.ownerDocument || xmlDocument);
            var xmlDocumentFragment = xmlDocument.createDocumentFragment();
            while (node) {
                xmlDocumentFragment.appendChild(xmlDocument.importNode(node, true));
                node = node.nextSibling;
            }
            xmlDocument.appendChild(xmlDocumentFragment);
        }
        setReadyState(xmlDocument, 3);
        var reason = cordys.getNodeText(oDoc, ".//*[local-name() ='parsererror']", null);
        if (!oDoc.documentElement || reason) {
            var errorExp = /([\s\S]*?)Location:([\s\S]*?)Line Number (\d+), Column (\d+):([\s\S]*?)(?:\-*\^)/g;
            errorExp.test(reason);
            xmlDocument.parseError = {
                "errorCode": -1,
                "line": RegExp.$3,
                "linepos": RegExp.$4,
                "reason": RegExp.$1 || reason,
                "srcText": RegExp.$5 || xmlString,
                "url": RegExp.$2 || document.location
            };
        }
        setReadyState(xmlDocument, 4);
    }
    return xmlDocument;

}

cordys.createElementNS = function(xmlDocument, namespaceURI, qualifiedName) {
    if (cordys.Browser.isIE) {
        return xmlDocument.createNode(1, qualifiedName, namespaceURI); //NOMBV
    } else {
        var elementNode = xmlDocument.createElementNS(namespaceURI, qualifiedName);
        if (!namespaceURI) {
            var namespaceAttr = xmlDocument.createAttribute("xmlns");
            namespaceAttr.value = "";
            elementNode.attributes.setNamedItem(namespaceAttr);
        }
        return elementNode;
    }
}

cordys.appendXMLNode = function(fromNode, toNode) {
    if (cordys.Browser.isIE) return toNode.appendChild(fromNode);

    var toNodeDocument = toNode.ownerDocument || toNode;
    if (fromNode.ownerDocument != toNodeDocument) {
        fromNode = toNodeDocument.adoptNode(fromNode);
    }
    var toNodeDocFragment = toNodeDocument.createDocumentFragment();
    var newNode = toNodeDocFragment.appendChild(fromNode);
    toNode.appendChild(toNodeDocFragment);
    return newNode;
}

cordys.xml2nicestring = function(xml, level, htmlObject, allowSelfClosingTags) {
    if (typeof(allowSelfClosingTags) == "undefined") {
        allowSelfClosingTags = true;
    }
    var s = [];
    if (typeof(level) == "undefined") level = 0;
    var tabs = "";
    while (level--) {
        tabs += "  ";
    }

    __xml2nicestring(xml, true, s, "", tabs, allowSelfClosingTags);
    var prettyXmlString = s.join("");

    if (htmlObject) {
        if (htmlObject.tagName == "TEXTAREA") {
            htmlObject.value = prettyXmlString;
        } else {
            cordys.setTextContent(htmlObject, prettyXmlString);
        }
    }

    return prettyXmlString;
}

cordys.getEventTarget = function(eventObject) {
    return eventObject.__cordysCustomEvent ? eventObject.srcElement : ((eventObject.target.nodeType == 1) ? eventObject.target : eventObject.target.parentNode); //NOMBV(2)
}

cordys.addDOMListener = function(element, eventType, handler) {
    return element.addEventListener(eventType.substr(2), handler, false);
}

cordys.removeDOMListener = function(element, eventType, handler) {
    return element.removeEventListener(eventType.substr(2), handler, false);
}

cordys.getComputedStyle = function(element) {
    if (element && typeof(element) != "undefined") {
        return element.currentStyle; //NOMBV
    }
    return null;
}

function setReadyState(xmlDocument, iReadyState) {
    xmlDocument._readyState = iReadyState;
    if (xmlDocument.onreadystatechange != null && typeof xmlDocument.onreadystatechange == "function") xmlDocument.onreadystatechange();
}

function cleanWhitespace(element) {
    if (element) {
        var child = element.firstChild;
        while (child) {
            var nextSibling = child.nextSibling;
            if ((child.nodeType == 3) && !(/[^\t\n\r ]/.test(child.data))) { /* Don't clear the whitespace when it a single child of spaces (without siblings and without newlines) to avoid cleaning <a>   </a>*/
                if (child.nextSibling || child.previousSibling || /[\n\r]/.test(child.data)) {
                    element.removeChild(child);
                }
            } else {
                cleanWhitespace(child);
            }
            child = nextSibling;
        }
    }
}